/*
 * Helpers for formatting and printing strings
 *
 * Copyright 31 August 2008 James Bottomley
 * Copyright (C) 2013, Intel Corporation
 */
#include <seminix/kernel.h>
#include <seminix/ctype.h>
#include <seminix/kstrtox.h>

/**
 * string_get_size - get the size in the specified units
 * @size:	The size to be converted in blocks
 * @blk_size:	Size of the block (use 1 for size in bytes)
 * @units:	units to use (powers of 1000 or 1024)
 * @buf:	buffer to format to
 * @len:	length of buffer
 *
 * This function returns a string formatted to 3 significant figures
 * giving the size in the required units.  @buf should have room for
 * at least 9 bytes and will always be zero terminated.
 *
 */
void string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
    char *buf, int len)
{
    static const char *const units_10[] = {
        "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
    };
    static const char *const units_2[] = {
        "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"
    };
    static const char *const *const units_str[] = {
        [STRING_UNITS_10] = units_10,
        [STRING_UNITS_2] = units_2,
    };
    static const unsigned int divisor[] = {
        [STRING_UNITS_10] = 1000,
        [STRING_UNITS_2] = 1024,
    };
    static const unsigned int rounding[] = { 500, 50, 5 };
    int i = 0, j;
    u32 remainder = 0, sf_cap;
    char tmp[8];
    const char *unit;

    tmp[0] = '\0';

    if (blk_size == 0)
        size = 0;
    if (size == 0)
        goto out;

    /* This is Napier's algorithm.  Reduce the original block size to
     *
     * coefficient * divisor[units]^i
     *
     * we do the reduction so both coefficients are just under 32 bits so
     * that multiplying them together won't overflow 64 bits and we keep
     * as much precision as possible in the numbers.
     *
     * Note: it's safe to throw away the remainders here because all the
     * precision is in the coefficients.
     */
    while (blk_size >> 32) {
        do_div(blk_size, divisor[units]);
        i++;
    }

    while (size >> 32) {
        do_div(size, divisor[units]);
        i++;
    }

    /* now perform the actual multiplication keeping i as the sum of the
     * two logarithms */
    size *= blk_size;

    /* and logarithmically reduce it until it's just under the divisor */
    while (size >= divisor[units]) {
        remainder = do_div(size, divisor[units]);
        i++;
    }

    /* work out in j how many digits of precision we need from the
     * remainder */
    sf_cap = size;
    for (j = 0; sf_cap*10 < 1000; j++)
        sf_cap *= 10;

    if (units == STRING_UNITS_2) {
        /* express the remainder as a decimal.  It's currently the
         * numerator of a fraction whose denominator is
         * divisor[units], which is 1 << 10 for STRING_UNITS_2 */
        remainder *= 1000;
        remainder >>= 10;
    }

    /* add a 5 to the digit below what will be printed to ensure
     * an arithmetical round up and carry it through to size */
    remainder += rounding[j];
    if (remainder >= 1000) {
        remainder -= 1000;
        size += 1;
    }

    if (j) {
        snprintf(tmp, sizeof(tmp), ".%03u", remainder);
        tmp[j+1] = '\0';
    }

 out:
    if (i >= (int)ARRAY_SIZE(units_2))
        unit = "UNK";
    else
        unit = units_str[units][i];

    snprintf(buf, len, "%u%s %s", (u32)size,
         tmp, unit);
}

static bool unescape_space(char **src, char **dst)
{
    char *p = *dst, *q = *src;

    switch (*q) {
    case 'n':
        *p = '\n';
        break;
    case 'r':
        *p = '\r';
        break;
    case 't':
        *p = '\t';
        break;
    case 'v':
        *p = '\v';
        break;
    case 'f':
        *p = '\f';
        break;
    default:
        return false;
    }
    *dst += 1;
    *src += 1;
    return true;
}

static bool unescape_octal(char **src, char **dst)
{
    char *p = *dst, *q = *src;
    u8 num;

    if (isodigit(*q) == 0)
        return false;

    num = (*q++) & 7;
    while (num < 32 && isodigit(*q) && (q - *src < 3)) {
        num <<= 3;
        num += (*q++) & 7;
    }
    *p = num;
    *dst += 1;
    *src = q;
    return true;
}

static bool unescape_hex(char **src, char **dst)
{
    char *p = *dst, *q = *src;
    int digit;
    u8 num;

    if (*q++ != 'x')
        return false;

    num = digit = hex_to_bin(*q++);
    if (digit < 0)
        return false;

    digit = hex_to_bin(*q);
    if (digit >= 0) {
        q++;
        num = (num << 4) | digit;
    }
    *p = num;
    *dst += 1;
    *src = q;
    return true;
}

static bool unescape_special(char **src, char **dst)
{
    char *p = *dst, *q = *src;

    switch (*q) {
    case '\"':
        *p = '\"';
        break;
    case '\\':
        *p = '\\';
        break;
    case 'a':
        *p = '\a';
        break;
    case 'e':
        *p = '\e';
        break;
    default:
        return false;
    }
    *dst += 1;
    *src += 1;
    return true;
}

/**
 * string_unescape - unquote characters in the given string
 * @src:	source buffer (escaped)
 * @dst:	destination buffer (unescaped)
 * @size:	size of the destination buffer (0 to unlimit)
 * @flags:	combination of the flags (bitwise OR):
 *	%UNESCAPE_SPACE:
 *		'\f' - form feed
 *		'\n' - new line
 *		'\r' - carriage return
 *		'\t' - horizontal tab
 *		'\v' - vertical tab
 *	%UNESCAPE_OCTAL:
 *		'\NNN' - byte with octal value NNN (1 to 3 digits)
 *	%UNESCAPE_HEX:
 *		'\xHH' - byte with hexadecimal value HH (1 to 2 digits)
 *	%UNESCAPE_SPECIAL:
 *		'\"' - double quote
 *		'\\' - backslash
 *		'\a' - alert (BEL)
 *		'\e' - escape
 *	%UNESCAPE_ANY:
 *		all previous together
 *
 * Description:
 * The function unquotes characters in the given string.
 *
 * Because the size of the output will be the same as or less than the size of
 * the input, the transformation may be performed in place.
 *
 * Caller must provide valid source and destination pointers. Be aware that
 * destination buffer will always be NULL-terminated. Source string must be
 * NULL-terminated as well.
 *
 * Return:
 * The amount of the characters processed to the destination buffer excluding
 * trailing '\0' is returned.
 */
int string_unescape(char *src, char *dst, usize size, unsigned int flags)
{
    char *out = dst;

    while (*src && --size) {
        if (src[0] == '\\' && src[1] != '\0' && size > 1) {
            src++;
            size--;

            if (flags & UNESCAPE_SPACE &&
                    unescape_space(&src, &out))
                continue;

            if (flags & UNESCAPE_OCTAL &&
                    unescape_octal(&src, &out))
                continue;

            if (flags & UNESCAPE_HEX &&
                    unescape_hex(&src, &out))
                continue;

            if (flags & UNESCAPE_SPECIAL &&
                    unescape_special(&src, &out))
                continue;

            *out++ = '\\';
        }
        *out++ = *src++;
    }
    *out = '\0';

    return out - dst;
}

static bool escape_passthrough(unsigned char c, char **dst, char *end)
{
    char *out = *dst;

    if (out < end)
        *out = c;
    *dst = out + 1;
    return true;
}

static bool escape_space(unsigned char c, char **dst, char *end)
{
    char *out = *dst;
    unsigned char to;

    switch (c) {
    case '\n':
        to = 'n';
        break;
    case '\r':
        to = 'r';
        break;
    case '\t':
        to = 't';
        break;
    case '\v':
        to = 'v';
        break;
    case '\f':
        to = 'f';
        break;
    default:
        return false;
    }

    if (out < end)
        *out = '\\';
    ++out;
    if (out < end)
        *out = to;
    ++out;

    *dst = out;
    return true;
}

static bool escape_special(unsigned char c, char **dst, char *end)
{
    char *out = *dst;
    unsigned char to;

    switch (c) {
    case '\\':
        to = '\\';
        break;
    case '\a':
        to = 'a';
        break;
    case '\e':
        to = 'e';
        break;
    default:
        return false;
    }

    if (out < end)
        *out = '\\';
    ++out;
    if (out < end)
        *out = to;
    ++out;

    *dst = out;
    return true;
}

static bool escape_null(unsigned char c, char **dst, char *end)
{
    char *out = *dst;

    if (c)
        return false;

    if (out < end)
        *out = '\\';
    ++out;
    if (out < end)
        *out = '0';
    ++out;

    *dst = out;
    return true;
}

static bool escape_octal(unsigned char c, char **dst, char *end)
{
    char *out = *dst;

    if (out < end)
        *out = '\\';
    ++out;
    if (out < end)
        *out = ((c >> 6) & 0x07) + '0';
    ++out;
    if (out < end)
        *out = ((c >> 3) & 0x07) + '0';
    ++out;
    if (out < end)
        *out = ((c >> 0) & 0x07) + '0';
    ++out;

    *dst = out;
    return true;
}

static bool escape_hex(unsigned char c, char **dst, char *end)
{
    char *out = *dst;

    if (out < end)
        *out = '\\';
    ++out;
    if (out < end)
        *out = 'x';
    ++out;
    if (out < end)
        *out = hex_asc_hi(c);
    ++out;
    if (out < end)
        *out = hex_asc_lo(c);
    ++out;

    *dst = out;
    return true;
}

/**
 * string_escape_mem - quote characters in the given memory buffer
 * @src:	source buffer (unescaped)
 * @isz:	source buffer size
 * @dst:	destination buffer (escaped)
 * @osz:	destination buffer size
 * @flags:	combination of the flags (bitwise OR):
 *	%ESCAPE_SPACE: (special white space, not space itself)
 *		'\f' - form feed
 *		'\n' - new line
 *		'\r' - carriage return
 *		'\t' - horizontal tab
 *		'\v' - vertical tab
 *	%ESCAPE_SPECIAL:
 *		'\\' - backslash
 *		'\a' - alert (BEL)
 *		'\e' - escape
 *	%ESCAPE_NULL:
 *		'\0' - null
 *	%ESCAPE_OCTAL:
 *		'\NNN' - byte with octal value NNN (3 digits)
 *	%ESCAPE_ANY:
 *		all previous together
 *	%ESCAPE_NP:
 *		escape only non-printable characters (checked by isprint)
 *	%ESCAPE_ANY_NP:
 *		all previous together
 *	%ESCAPE_HEX:
 *		'\xHH' - byte with hexadecimal value HH (2 digits)
 * @only:	NULL-terminated string containing characters used to limit
 *		the selected escape class. If characters are included in @only
 *		that would not normally be escaped by the classes selected
 *		in @flags, they will be copied to @dst unescaped.
 *
 * Description:
 * The process of escaping byte buffer includes several parts. They are applied
 * in the following sequence.
 *	1. The character is matched to the printable class, if asked, and in
 *	   case of match it passes through to the output.
 *	2. The character is not matched to the one from @only string and thus
 *	   must go as-is to the output.
 *	3. The character is checked if it falls into the class given by @flags.
 *	   %ESCAPE_OCTAL and %ESCAPE_HEX are going last since they cover any
 *	   character. Note that they actually can't go together, otherwise
 *	   %ESCAPE_HEX will be ignored.
 *
 * Caller must provide valid source and destination pointers. Be aware that
 * destination buffer will not be NULL-terminated, thus caller have to append
 * it if needs.
 *
 * Return:
 * The total size of the escaped output that would be generated for
 * the given input and flags. To check whether the output was
 * truncated, compare the return value to osz. There is room left in
 * dst for a '\0' terminator if and only if ret < osz.
 */
int string_escape_mem(const char *src, usize isz, char *dst, usize osz,
              unsigned int flags, const char *only)
{
    char *p = dst;
    char *end = p + osz;
    bool is_dict = only && *only;

    while (isz--) {
        unsigned char c = *src++;

        /*
         * Apply rules in the following sequence:
         *	- the character is printable, when @flags has
         *	  %ESCAPE_NP bit set
         *	- the @only string is supplied and does not contain a
         *	  character under question
         *	- the character doesn't fall into a class of symbols
         *	  defined by given @flags
         * In these cases we just pass through a character to the
         * output buffer.
         */
        if ((flags & ESCAPE_NP && isprint(c)) ||
            (is_dict && !strchr(only, c))) {
            /* do nothing */
        } else {
            if (flags & ESCAPE_SPACE && escape_space(c, &p, end))
                continue;

            if (flags & ESCAPE_SPECIAL && escape_special(c, &p, end))
                continue;

            if (flags & ESCAPE_NULL && escape_null(c, &p, end))
                continue;

            /* ESCAPE_OCTAL and ESCAPE_HEX always go last */
            if (flags & ESCAPE_OCTAL && escape_octal(c, &p, end))
                continue;

            if (flags & ESCAPE_HEX && escape_hex(c, &p, end))
                continue;
        }

        escape_passthrough(c, &p, end);
    }

    return p - dst;
}

/**
 * simple_strtoull - convert a string to an unsigned long long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 *
 * This function has caveats. Please use kstrtoull instead.
 */
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
{
    unsigned long long result;
    unsigned int rv;

    cp = _parse_integer_fixup_radix(cp, &base);
    rv = _parse_integer(cp, base, &result);
    /* FIXME */
    cp += (rv & ~KSTRTOX_OVERFLOW);

    if (endp)
        *endp = (char *)cp;

    return result;
}

/**
 * simple_strtoul - convert a string to an unsigned long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 *
 * This function has caveats. Please use kstrtoul instead.
 */
unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
{
    return simple_strtoull(cp, endp, base);
}

/**
 * simple_strtol - convert a string to a signed long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 *
 * This function has caveats. Please use kstrtol instead.
 */
long simple_strtol(const char *cp, char **endp, unsigned int base)
{
    if (*cp == '-')
        return -simple_strtoul(cp + 1, endp, base);

    return simple_strtoul(cp, endp, base);
}

/**
 * simple_strtoll - convert a string to a signed long long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 *
 * This function has caveats. Please use kstrtoll instead.
 */
long long simple_strtoll(const char *cp, char **endp, unsigned int base)
{
    if (*cp == '-')
        return -simple_strtoull(cp + 1, endp, base);

    return simple_strtoull(cp, endp, base);
}

/*
 * Decimal conversion is by far the most typical, and is used for
 * /proc and /sys data. This directly impacts e.g. top performance
 * with many processes running. We optimize it for speed by emitting
 * two characters at a time, using a 200 byte lookup table. This
 * roughly halves the number of multiplications compared to computing
 * the digits one at a time. Implementation strongly inspired by the
 * previous version, which in turn used ideas described at
 * <http://www.cs.uiowa.edu/~jones/bcd/divide.html> (with permission
 * from the author, Douglas W. Jones).
 *
 * It turns out there is precisely one 26 bit fixed-point
 * approximation a of 64/100 for which x/100 == (x * (u64)a) >> 32
 * holds for all x in [0, 10^8-1], namely a = 0x28f5c29. The actual
 * range happens to be somewhat larger (x <= 1073741898), but that's
 * irrelevant for our purpose.
 *
 * For dividing a number in the range [10^4, 10^6-1] by 100, we still
 * need a 32x32->64 bit multiply, so we simply use the same constant.
 *
 * For dividing a number in the range [100, 10^4-1] by 100, there are
 * several options. The simplest is (x * 0x147b) >> 19, which is valid
 * for all x <= 43698.
 */

static const u16 decpair[100] = {
#define _(x) (u16) cpu_to_le16(((x % 10) | ((x / 10) << 8)) + 0x3030)
    _( 0), _( 1), _( 2), _( 3), _( 4), _( 5), _( 6), _( 7), _( 8), _( 9),
    _(10), _(11), _(12), _(13), _(14), _(15), _(16), _(17), _(18), _(19),
    _(20), _(21), _(22), _(23), _(24), _(25), _(26), _(27), _(28), _(29),
    _(30), _(31), _(32), _(33), _(34), _(35), _(36), _(37), _(38), _(39),
    _(40), _(41), _(42), _(43), _(44), _(45), _(46), _(47), _(48), _(49),
    _(50), _(51), _(52), _(53), _(54), _(55), _(56), _(57), _(58), _(59),
    _(60), _(61), _(62), _(63), _(64), _(65), _(66), _(67), _(68), _(69),
    _(70), _(71), _(72), _(73), _(74), _(75), _(76), _(77), _(78), _(79),
    _(80), _(81), _(82), _(83), _(84), _(85), _(86), _(87), _(88), _(89),
    _(90), _(91), _(92), _(93), _(94), _(95), _(96), _(97), _(98), _(99),
#undef _
};

/*
 * This will print a single '0' even if r == 0, since we would
 * immediately jump to out_r where two 0s would be written but only
 * one of them accounted for in buf. This is needed by ip4_string
 * below. All other callers pass a non-zero value of r.
*/
static
char *put_dec_trunc8(char *buf, unsigned r)
{
    unsigned q;

    /* 1 <= r < 10^8 */
    if (r < 100)
        goto out_r;

    /* 100 <= r < 10^8 */
    q = (r * (u64)0x28f5c29) >> 32;
    *((u16 *)buf) = decpair[r - 100*q];
    buf += 2;

    /* 1 <= q < 10^6 */
    if (q < 100)
        goto out_q;

    /*  100 <= q < 10^6 */
    r = (q * (u64)0x28f5c29) >> 32;
    *((u16 *)buf) = decpair[q - 100*r];
    buf += 2;

    /* 1 <= r < 10^4 */
    if (r < 100)
        goto out_r;

    /* 100 <= r < 10^4 */
    q = (r * 0x147b) >> 19;
    *((u16 *)buf) = decpair[r - 100*q];
    buf += 2;
out_q:
    /* 1 <= q < 100 */
    r = q;
out_r:
    /* 1 <= r < 100 */
    *((u16 *)buf) = decpair[r];
    buf += r < 10 ? 1 : 2;
    return buf;
}

#if BITS_PER_LONG == 64 && BITS_PER_LONG_LONG == 64
static
char *put_dec_full8(char *buf, unsigned r)
{
    unsigned q;

    /* 0 <= r < 10^8 */
    q = (r * (u64)0x28f5c29) >> 32;
    *((u16 *)buf) = decpair[r - 100*q];
    buf += 2;

    /* 0 <= q < 10^6 */
    r = (q * (u64)0x28f5c29) >> 32;
    *((u16 *)buf) = decpair[q - 100*r];
    buf += 2;

    /* 0 <= r < 10^4 */
    q = (r * 0x147b) >> 19;
    *((u16 *)buf) = decpair[r - 100*q];
    buf += 2;

    /* 0 <= q < 100 */
    *((u16 *)buf) = decpair[q];
    buf += 2;
    return buf;
}

char *put_dec(char *buf, unsigned long long n)
{
    if (n >= 100*1000*1000)
        buf = put_dec_full8(buf, do_div(n, 100*1000*1000));
    /* 1 <= n <= 1.6e11 */
    if (n >= 100*1000*1000)
        buf = put_dec_full8(buf, do_div(n, 100*1000*1000));
    /* 1 <= n < 1e8 */
    return put_dec_trunc8(buf, n);
}

#elif BITS_PER_LONG == 32 && BITS_PER_LONG_LONG == 64

static void
put_dec_full4(char *buf, unsigned r)
{
    unsigned q;

    /* 0 <= r < 10^4 */
    q = (r * 0x147b) >> 19;
    *((u16 *)buf) = decpair[r - 100*q];
    buf += 2;
    /* 0 <= q < 100 */
    *((u16 *)buf) = decpair[q];
}

/*
 * Call put_dec_full4 on x % 10000, return x / 10000.
 * The approximation x/10000 == (x * 0x346DC5D7) >> 43
 * holds for all x < 1,128,869,999.  The largest value this
 * helper will ever be asked to convert is 1,125,520,955.
 * (second call in the put_dec code, assuming n is all-ones).
 */
static
unsigned put_dec_helper4(char *buf, unsigned x)
{
        uint32_t q = (x * (uint64_t)0x346DC5D7) >> 43;

        put_dec_full4(buf, x - q * 10000);
        return q;
}

/* Based on code by Douglas W. Jones found at
 * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour>
 * (with permission from the author).
 * Performs no 64-bit division and hence should be fast on 32-bit machines.
 */
char *put_dec(char *buf, unsigned long long n)
{
    uint32_t d3, d2, d1, q, h;

    if (n < 100*1000*1000)
        return put_dec_trunc8(buf, n);

    d1  = ((uint32_t)n >> 16); /* implicit "& 0xffff" */
    h   = (n >> 32);
    d2  = (h      ) & 0xffff;
    d3  = (h >> 16); /* implicit "& 0xffff" */

    /* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d0
         = 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */
    q   = 656 * d3 + 7296 * d2 + 5536 * d1 + ((uint32_t)n & 0xffff);
    q = put_dec_helper4(buf, q);

    q += 7671 * d3 + 9496 * d2 + 6 * d1;
    q = put_dec_helper4(buf+4, q);

    q += 4749 * d3 + 42 * d2;
    q = put_dec_helper4(buf+8, q);

    q += 281 * d3;
    buf += 12;
    if (q)
        buf = put_dec_trunc8(buf, q);
    else while (buf[-1] == '0')
        --buf;

    return buf;
}

#endif

/*
 * Convert passed number to decimal string.
 * Returns the length of string.  On buffer overflow, returns 0.
 *
 * If speed is not important, use snprintf(). It's easy to read the code.
 */
int num_to_str(char *buf, int size, unsigned long long num, int width)
{
    /* put_dec requires 2-byte alignment of the buffer. */
    char tmp[sizeof(num) * 3] __aligned(2);
    int idx;
    int len;

    /* put_dec() may work incorrectly for num = 0 (generate "", not "0") */
    if (num <= 9) {
        tmp[0] = '0' + num;
        len = 1;
    } else {
        len = put_dec(tmp, num) - tmp;
    }

    if (len > size || width > size)
        return 0;

    if (width > len) {
        width = width - len;
        for (idx = 0; idx < width; idx++)
            buf[idx] = ' ';
    } else {
        width = 0;
    }

    for (idx = 0; idx < len; ++idx)
        buf[idx + width] = tmp[len - idx - 1];

    return len + width;
}

/**
 * skip_spaces - Removes leading whitespace from @str.
 * @str: The string to be stripped.
 *
 * Returns a pointer to the first non-whitespace character in @str.
 */
char *skip_spaces(const char *str)
{
    while (isspace(*str))
        ++str;
    return (char *)str;
}

/**
 * strim - Removes leading and trailing whitespace from @s.
 * @s: The string to be stripped.
 *
 * Note that the first trailing whitespace is replaced with a %NUL-terminator
 * in the given string @s. Returns a pointer to the first non-whitespace
 * character in @s.
 */
char *strim(char *s)
{
    usize size;
    char *end;

    size = strlen(s);
    if (!size)
        return s;

    end = s + size - 1;
    while (end >= s && isspace(*end))
        end--;
    *(end + 1) = '\0';

    return skip_spaces(s);
}

/**
 * match_string - matches given string in an array
 * @array:	array of strings
 * @n:		number of strings in the array or -1 for NULL terminated arrays
 * @string:	string to match with
 *
 * Return:
 * index of a @string in the @array if matches, or %-EINVAL otherwise.
 */
int match_string(const char * const *array, usize n, const char *string)
{
    usize index;
    const char *item;

    for (index = 0; index < n; index++) {
        item = array[index];
        if (!item)
            break;
        if (!strcmp(item, string))
            return index;
    }

    return -1;
}

/**
 * strreplace - Replace all occurrences of character in string.
 * @s: The string to operate on.
 * @old: The character being replaced.
 * @new: The character @old is replaced with.
 *
 * Returns pointer to the nul byte at the end of @s.
 */
char *strreplace(char *s, char old, char new)
{
    for (; *s; ++s)
        if (*s == old)
            *s = new;
    return s;
}
